#include <netinet/ip.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "net.h"
#include "env.h"

static char intermed_buf[1 << 16]; // simply pre-allocate intermediate buffers

static int sendto_ipv4_ip_sockfd;

static void sendto_noconn(struct sockaddr_in *addr, const char* buf, size_t buflen, int sockfd)
{
	if (sendto(sockfd, buf, buflen, 0, (struct sockaddr*)addr, sizeof(*addr)) == -1) {
		perror("sendto");
		exit(EXIT_FAILURE);
	}

    printf("[+] sendto succeeded\n");
}

// proudly stolen from https://android.googlesource.com/platform/system/core/+/refs/heads/main/libnetutils/packet.c#62
static uint32_t ip_checksum(void *buffer, unsigned int count, uint32_t startsum)
{
    uint16_t *up = (uint16_t *)buffer;
    uint32_t sum = startsum;
    uint32_t upper16;

    while (count > 1) {
        sum += *up++;
        count -= 2;
    }
    
    if (count > 0)
        sum += (uint16_t) *(uint8_t *)up;
    
    while ((upper16 = (sum >> 16)) != 0)
        sum = (sum & 0xffff) + upper16;

    return sum;
}

static inline uint32_t ip_finish_sum(uint32_t sum)
{
    return ~sum & 0xffff;
}

void send_ipv4_ip_hdr(const char* buf, size_t buflen, struct ip *ip_header)
{
	size_t ip_buflen = sizeof(struct ip) + buflen;
    struct sockaddr_in dst_addr = {
		.sin_family = AF_INET,
		.sin_addr.s_addr =  inet_addr("127.0.0.2")  // 127.0.0.1 will not be ipfrag_time'd. this can't be set to 1.1.1.1 since C runtime will prob catch it
	};

    memcpy(intermed_buf, ip_header, sizeof(*ip_header));
	memcpy(&intermed_buf[sizeof(*ip_header)], buf, buflen);

	// checksum needds to be 0 before
	((struct ip*)intermed_buf)->ip_sum = 0;
	((struct ip*)intermed_buf)->ip_sum = ip_finish_sum(ip_checksum(intermed_buf, ip_buflen, 0));

	PRINTF_VERBOSE("[*] sending IP packet (%ld bytes)...\n", ip_buflen);

	sendto_noconn(&dst_addr, intermed_buf, ip_buflen, sendto_ipv4_ip_sockfd);
}

void populate_sockets()
{
	memset(intermed_buf, '\x00', sizeof(intermed_buf));

    sendto_ipv4_ip_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (sendto_ipv4_ip_sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
}